package independence.reflection
{
	import independence.core.IndyObject;

	/**
	 * The Field class represents an object's specific field: any public variable declared
	 * at the instance or class level. It provides strict-typed methods for getting and setting
	 * the value of represented by a certain field in an object, as well as the field's name
	 * and declaring class.
	 * 
	 * @author TK, TK Assembled for Independence Glory.
	 */
	public final class Field extends IndyObject implements IReadableMember, IWritableMember
	{		
		/**
		 * @protected The Class object associated with this Field.
		 */
		protected var clazz:Class;
		/**
		 * @protected The name of the object's field.
		 */
		protected var fieldName:String;
		/**
		 * @protected The class type of the field.
		 */
		protected var type:Class;
		/**
		 * @protected Whether this field is static or not.
		 */
		protected var statik:Boolean; 
		/**
		 * Constructs a new Field object.
		 * @param	clazz		The <code>Class</code> to which this field belongs.
		 * @param	fieldName	The String name of the field.
		 * @param	type		The Class type of the field.
		 * @param	statik		Whether this is a class-level field (true) or an instance-level field (false).
		 */
		public function Field(clazz:Class, fieldName:String, type:Class, statik:Boolean = false)
		{
			this.clazz = clazz;
			this.fieldName = fieldName;
			this.type = type;
			this.statik = statik;
		}
		/**
		 * Returns whether the Field is static (class-level) or not (instance-level).
		 * @return	True if the Field is static, false otherwise.
		 */
		public function isStatic():Boolean
		{
			return statik;
		}
		/**
		 * Returns the Class object which this field belongs to.
		 * @return	The class which this field belongs to.
		 */
		public function getDeclaringClass():Class
		{
			return this.clazz;
		}
		/**
		 * Returns the String name of the Field.
		 * @return	The name of the field.
		 */
		public function getName():String
		{
			return this.fieldName;
		}
		/**
		 * Returns the class type of this field.
		 * @return	The class type of this field.
		 */
		public function getType():Class
		{
			return this.type;
		}
		/**
		 * Gets the value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this field.
		 * @return	The value of this field in the <code>target</code> object.
		 */
		public function getValue(target:Object):Object 
		{
			return statik ? clazz[fieldName] : target[fieldName];
		}
		/**
		 * Get the Array value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this field.
		 * @return	The Array value of this field in the <code>target</code> object.
		 */
		public function getArray(target:Object):Array 
		{
			return this.getValue(target) as Array;
		}
		/**
		 * Gets the boolean value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this field.
		 * @return	The boolean value of this field in the <code>target</code> object.
		 */
		public function getBoolean(target:Object):Boolean
		{
			return Boolean(getValue(target));
		}
		/**
		 * Gets the integer value (int) of this field in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this field.
		 * @return	The integer value of this field in the <code>target</code> object.
		 */
		public function getInteger(target:Object):int
		{
			return int(getValue(target));
		}
		/**
		 * @inheritDoc
		 */
		public function getNumber(target:Object):Number
		{
			return Number(getValue(target));
		}
		/**
		 * Gets the string value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this field.
		 * @return	The string value of this field in the <code>target</code> object.
		 */
		public function getString(target:Object):String
		{
			return String(getValue(target));
		}
		/**
		 * Gets the unsigned integer (uint) value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this field.
		 * @return	The unsigned integer value of this field in the <code>target</code> object.
		 */
		public function getUnsignedInt(target:Object):uint
		{
			return uint(getValue(target));
		}
		/**
		 * Gets the XML value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this field.
		 * @return	The XML value of this field in the <code>target</code> object.
		 */
		public function getXML(target:Object):XML
		{
			return XML(getValue(target));
		}
		/**
		 * Sets the value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this field.
		 * @param	value	The value to set the field to.
		 */
		public function setValue(target:Object, value:Object):void
		{
			statik ? clazz[fieldName] = value : target[fieldName] = value;
		}
		/**
		 * Sets the array value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this field.
		 * @param	value	The value to set the field to.
		 */		
		public function setArray(target:Object, value:Array):void
		{
			this.setValue(target, value);
		}	
		/**
		 * Sets the boolean value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this field.
		 * @param	value	The value to set the field to.
		 */		
		public function setBoolean(target:Object, value:Boolean):void
		{
			this.setValue(target, value);
		}	
		/**
		 * Sets the integer value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this field.
		 * @param	value	The value to set the field to.
		 */		
		public function setInteger(target:Object, value:int):void
		{
			this.setValue(target, value);	
		}
		/**
		 * @inheritDoc
		 */
		public function setNumber(target:Object, value:Number):void
		{
			this.setValue(target, value);
		}
		/**
		 * Sets the string value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this field.
		 * @param	value	The value to set the field to.
		 */		
		public function setString(target:Object, value:String):void
		{
			this.setValue(target, value);
		}
		/**
		 * Sets the unsigned integer value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this field.
		 * @param	value	The value to set the field to.
		 */		
		public function setUnsignedInt(target:Object, value:uint):void
		{
			this.setValue(target, value);
		}
		/**
		 * Sets the XML value of this field in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this field.
		 * @param	value	The value to set the field to.
		 */		
		public function setXML(target:Object, value:XML):void
		{
			this.setValue(target, value);
		}
		/**
		 * Returns a String representation of this <code>Field</code> object.
		 * @return	A String representation of this <code>Field</code>.
		 */
		public function toString():String
		{
			return "[Field " + this.fieldName + "]";
		}
	}
}